/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.builder;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.mapping.ResultSetType;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeAliasRegistry;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;

/**
 * 配置文件解析器基类（抽象类），主要提供了一些辅助方法。
 * 1、{@link #parseExpression(String, String)} 根据正则表达式生成Pattern对象
 * 2、{@link #booleanValueOf(String, Boolean)} 根据字符串转Boolean对象true或false
 * 3、{@link #integerValueOf(String, Integer)} 根据字符串转Integer对象
 * 4、{@link #stringSetValueOf(String, String)} 根据字符串转Set<String>对象,字符串用英文逗号分隔“,”
 * 5、{@link #resolveJdbcType(String)} 根据字符串解析对应的JdbcType类型
 * 6、{@link #resolveResultSetType(String)} 根据字符串解析对应的ResultSetType类型
 * 7、{@link #resolveParameterMode(String)} 根据字符串解析对应的ParameterMode类型
 * 8、{@link #createInstance(String)}、{@link #resolveClass(String)} 根据类别名（字符串）生成对应的实例对象或Class<?>实例
 * 9、{@link #resolveTypeHandler(Class, String)}、{@link #resolveTypeHandler(Class, Class)}根据javaType的Class实例和TypeHandler的别名字符串或Class<?>实例，获取对应的TypeHandler<?>对象
 * 10、{@link #resolveAlias(String)}	根据类的别名获取对应的Class<?>对象。
 * 通过Configuration实例构建对象，且Configuration实例对象中必须包括typeAliasRegistry和typeHandlerRegistry实例对象。
 * @author Clinton Begin
 */
public abstract class BaseBuilder {
	/**
	 * Configuration是MyBatis初始化过程的核心对象，MyBatis中几乎全部的配置信息会保存到Configuration 对象中。
	 */
  protected final Configuration configuration;
  /**
   * mybatis-config.xml配置文件中可以使用<typeAliases>标签定义别名，
   * 这些定义的别名都会记录在该TypeAliasRegistry对象中。在Configuration对象初始化时创建的。
   */
  protected final TypeAliasRegistry typeAliasRegistry;
  /**
        * 在mybatis-config.xml配置文件中可以使用<typeHandlers>标签添加自定义TypeHandler器，
        * 完成指定数据库类型与Java 类型的转换，这些TypeHandler都会记录在TypeHandlerRegistry中。
        * 在Configuration对象初始化时创建的。
   */
  protected final TypeHandlerRegistry typeHandlerRegistry;
  /**
        * 构造函数
   * 	参数：configuration
   * 	属性typeAliasRegistry和typeHandlerRegistry通过configuration对象获取。
   * @param configuration
   */
  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }
  /**
   * 获取configuration实例对象
   * @return
   */
  public Configuration getConfiguration() {
    return configuration;
  }
  /**
   * 根据正则表达式生成Pattern对象
   * @param regex 正则表达式
   * @param defaultValue 当regex=null时的默认正则表达式
   * @return
   */
  protected Pattern parseExpression(String regex, String defaultValue) {
    return Pattern.compile(regex == null ? defaultValue : regex);
  }
  /**
   * 根据字符串转Boolean对象true或false
   * @param value 字符串true或false
   * @param defaultValue	当value等于null时的默认值
   * @return
   */
  protected Boolean booleanValueOf(String value, Boolean defaultValue) {
    return value == null ? defaultValue : Boolean.valueOf(value);
  }
  /**
   * 根据字符串转Integer对象
   * @param value 字符串
   * @param defaultValue 当value=null时的默认值
   * @return
   */
  protected Integer integerValueOf(String value, Integer defaultValue) {
    return value == null ? defaultValue : Integer.valueOf(value);
  }
  /**
   * 根据字符串转Set<String>对象
   * @param value 逗号（","）分隔的字符串
   * @param defaultValue 当value=null时的默认值（格式也是逗号（","）分隔的字符串）
   * @return
   */
  protected Set<String> stringSetValueOf(String value, String defaultValue) {
    value = (value == null ? defaultValue : value);
    return new HashSet<String>(Arrays.asList(value.split(",")));
  }
  /**
   * 根据字符串解析对应的JdbcType类型
   * 	如果alias=null，则返回null。
   * @param alias 取值范围：JdbcType对应枚举值的字符串
   * @return
   */
  protected JdbcType resolveJdbcType(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      return JdbcType.valueOf(alias);
    } catch (IllegalArgumentException e) {
      throw new BuilderException("Error resolving JdbcType. Cause: " + e, e);
    }
  }
  /**
   * 根据字符串解析对应的ResultSetType类型
   * 	如果alias=null，则返回null。
   * @param alias 取值范围：FORWARD_ONLY,SCROLL_INSENSITIVE,SCROLL_SENSITIVE
   * @return
   */
  protected ResultSetType resolveResultSetType(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      return ResultSetType.valueOf(alias);
    } catch (IllegalArgumentException e) {
      throw new BuilderException("Error resolving ResultSetType. Cause: " + e, e);
    }
  }
  /**
   * 根据字符串解析对应的ParameterMode类型
   * 	如果alias=null，则返回null。
   * @param alias 取值返回：IN, OUT, INOUT
   * @return
   */
  protected ParameterMode resolveParameterMode(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      return ParameterMode.valueOf(alias);
    } catch (IllegalArgumentException e) {
      throw new BuilderException("Error resolving ParameterMode. Cause: " + e, e);
    }
  }
  /**
   * 根据类别名（字符串）生成对应的实例对象
   * 	底层通过typeAliasRegistry.resolveAlias(alias)方法实现。
   * @param alias
   * @return
   */
  protected Object createInstance(String alias) {
    Class<?> clazz = resolveClass(alias);
    if (clazz == null) {
      return null;
    }
    try {
      return resolveClass(alias).newInstance();
    } catch (Exception e) {
      throw new BuilderException("Error creating instance. Cause: " + e, e);
    }
  }
  /**
   * 根据类别名（字符串）生成对应的Class<?>实例
   * 	底层通过typeAliasRegistry.resolveAlias(alias)方法实现。
   * @param alias
   * @return
   */
  protected Class<?> resolveClass(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      return resolveAlias(alias);
    } catch (Exception e) {
      throw new BuilderException("Error resolving class. Cause: " + e, e);
    }
  }
  /**
   * 根据javaType的Class实例和TypeHandler的别名字符串，获取对应的TypeHandler<?>对象
   * 	底层通过typeHandlerRegistry方法获取，
   * 		首先通过TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);如果handler=null，
   * 		则通过handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);获取。
   * @param javaType  javaType的Class实例
   * @param typeHandlerAlias TypeHandler的别名字符串
   * @return
   */
  protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, String typeHandlerAlias) {
    if (typeHandlerAlias == null) {
      return null;
    }
    Class<?> type = resolveClass(typeHandlerAlias);
    if (type != null && !TypeHandler.class.isAssignableFrom(type)) {
      throw new BuilderException("Type " + type.getName() + " is not a valid TypeHandler because it does not implement TypeHandler interface");
    }
    @SuppressWarnings( "unchecked" ) // already verified it is a TypeHandler
    Class<? extends TypeHandler<?>> typeHandlerType = (Class<? extends TypeHandler<?>>) type;
    return resolveTypeHandler(javaType, typeHandlerType);
  }
  /**
   * 根据javaType的Class实例和TypeHandler的Class实例，获取对应的TypeHandler<?>对象
   * 	底层通过typeHandlerRegistry方法获取，
   * 		首先通过TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);如果handler=null，
   * 		则通过handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);获取。
   * @param javaType  javaType的Class实例
   * @param typeHandlerType TypeHandler的Class实例
   * @return
   */
  protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) {
    if (typeHandlerType == null) {
      return null;
    }
    // javaType ignored for injected handlers see issue #746 for full detail
    TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
    if (handler == null) {
      // not in registry, create a new one
      handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);
    }
    return handler;
  }
  /**
   * 根据类的别名获取对应的Class<?>对象。
   * 	底层通过typeAliasRegistry.resolveAlias(alias)方法实现。
   * @param alias
   * @return
   */
  protected Class<?> resolveAlias(String alias) {
    return typeAliasRegistry.resolveAlias(alias);
  }
}
